// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef NET_TOOLS_FLIP_SERVER_SPDY_INTERFACE_H_
#define NET_TOOLS_FLIP_SERVER_SPDY_INTERFACE_H_

#include <map>
#include <string>
#include <vector>

#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "net/spdy/buffered_spdy_framer.h"
#include "net/spdy/spdy_protocol.h"
#include "net/tools/balsa/balsa_headers.h"
#include "net/tools/balsa/balsa_visitor_interface.h"
#include "net/tools/flip_server/output_ordering.h"
#include "net/tools/flip_server/sm_connection.h"
#include "net/tools/flip_server/sm_interface.h"

namespace net {

class FlipAcceptor;
class MemoryCache;

class SpdySM : public BufferedSpdyFramerVisitorInterface, public SMInterface {
 public:
  SpdySM(SMConnection* connection,
         SMInterface* sm_http_interface,
         EpollServer* epoll_server,
         MemoryCache* memory_cache,
         FlipAcceptor* acceptor,
         SpdyMajorVersion spdy_version);
  ~SpdySM() override;

  void InitSMInterface(SMInterface* sm_http_interface,
                       int32 server_idx) override {}

  void InitSMConnection(SMConnectionPoolInterface* connection_pool,
                        SMInterface* sm_interface,
                        EpollServer* epoll_server,
                        int fd,
                        std::string server_ip,
                        std::string server_port,
                        std::string remote_ip,
                        bool use_ssl) override;

  // Create new SPDY framer after reusing SpdySM and negotiating new version
  void CreateFramer(SpdyMajorVersion spdy_version);

 private:
  void set_is_request() override {}
  SMInterface* NewConnectionInterface();
  // virtual for tests
  virtual SMInterface* FindOrMakeNewSMConnectionInterface(
      const std::string& server_ip,
      const std::string& server_port);
  int SpdyHandleNewStream(SpdyStreamId stream_id,
                          SpdyPriority priority,
                          const SpdyHeaderBlock& headers,
                          std::string& http_data,
                          bool* is_https_scheme);

  // BufferedSpdyFramerVisitorInterface:
  void OnError(SpdyFramer::SpdyError error_code) override {}
  void OnStreamError(SpdyStreamId stream_id,
                     const std::string& description) override {}
  // Called after all the header data for SYN_STREAM control frame is received.
  void OnSynStream(SpdyStreamId stream_id,
                   SpdyStreamId associated_stream_id,
                   SpdyPriority priority,
                   bool fin,
                   bool unidirectional,
                   const SpdyHeaderBlock& headers) override;

  // Called after all the header data for SYN_REPLY control frame is received.
  void OnSynReply(SpdyStreamId stream_id,
                  bool fin,
                  const SpdyHeaderBlock& headers) override;

  // Called after all the header data for HEADERS control frame is received.
  void OnHeaders(SpdyStreamId stream_id,
                 bool has_priority,
                 SpdyPriority priority,
                 bool fin,
                 const SpdyHeaderBlock& headers) override;

  // Called when data frame header is received.
  void OnDataFrameHeader(SpdyStreamId stream_id,
                         size_t length,
                         bool fin) override {}

  // Called when data is received.
  // |stream_id| The stream receiving data.
  // |data| A buffer containing the data received.
  // |len| The length of the data buffer.
  // When the other side has finished sending data on this stream,
  // this method will be called with a zero-length buffer.
  void OnStreamFrameData(SpdyStreamId stream_id,
                         const char* data,
                         size_t len,
                         bool fin) override;

  // Called when padding is received (padding length field or padding octets).
  // |stream_id| The stream receiving data.
  // |len| The number of padding octets.
  void OnStreamPadding(SpdyStreamId stream_id, size_t len) override;

  // Called when a SETTINGS frame is received.
  // |clear_persisted| True if the respective flag is set on the SETTINGS frame.
  void OnSettings(bool clear_persisted) override {}

  // Called when an individual setting within a SETTINGS frame has been parsed
  // and validated.
  void OnSetting(SpdySettingsIds id, uint8 flags, uint32 value) override {}

  // Called when a PING frame has been parsed.
  void OnPing(SpdyPingId unique_id, bool is_ack) override {}

  // Called when a RST_STREAM frame has been parsed.
  void OnRstStream(SpdyStreamId stream_id, SpdyRstStreamStatus status) override;

  // Called when a GOAWAY frame has been parsed.
  void OnGoAway(SpdyStreamId last_accepted_stream_id,
                SpdyGoAwayStatus status) override {}

  // Called when a WINDOW_UPDATE frame has been parsed.
  void OnWindowUpdate(SpdyStreamId stream_id,
                      uint32 delta_window_size) override {}

  // Called when a PUSH_PROMISE frame has been parsed.
  void OnPushPromise(SpdyStreamId stream_id,
                     SpdyStreamId promised_stream_id,
                     const SpdyHeaderBlock& headers) override {}

  bool OnUnknownFrame(SpdyStreamId stream_id, int frame_type) override;

 public:
  size_t ProcessReadInput(const char* data, size_t len) override;
  size_t ProcessWriteInput(const char* data, size_t len) override;
  bool MessageFullyRead() const override;
  void SetStreamID(uint32 stream_id) override {}
  bool Error() const override;
  const char* ErrorAsString() const override;
  void Reset() override {}
  void ResetForNewInterface(int32 server_idx) override;
  void ResetForNewConnection() override;
  // SMInterface's Cleanup is currently only called by SMConnection after a
  // protocol message as been fully read. Spdy's SMInterface does not need
  // to do any cleanup at this time.
  // TODO(klindsay) This method is probably not being used properly and
  // some logic review and method renaming is probably in order.
  void Cleanup() override {}
  // Send a settings frame
  int PostAcceptHook() override;
  void NewStream(uint32 stream_id,
                 uint32 priority,
                 const std::string& filename) override;
  void AddToOutputOrder(const MemCacheIter& mci);
  void SendEOF(uint32 stream_id) override;
  void SendErrorNotFound(uint32 stream_id) override;
  size_t SendSynStream(uint32 stream_id, const BalsaHeaders& headers) override;
  size_t SendSynReply(uint32 stream_id, const BalsaHeaders& headers) override;
  void SendDataFrame(uint32 stream_id,
                     const char* data,
                     int64 len,
                     uint32 flags,
                     bool compress) override;
  BufferedSpdyFramer* spdy_framer() { return buffered_spdy_framer_.get(); }

  const OutputOrdering& output_ordering() const {
    return client_output_ordering_;
  }

  static std::string forward_ip_header() { return forward_ip_header_; }
  static void set_forward_ip_header(const std::string& value) {
    forward_ip_header_ = value;
  }
  SpdyMajorVersion spdy_version() const {
    DCHECK(buffered_spdy_framer_);
    return buffered_spdy_framer_->protocol_version();
  }

 private:
  void SendEOFImpl(uint32 stream_id);
  void SendErrorNotFoundImpl(uint32 stream_id);
  void KillStream(uint32 stream_id);
  void CopyHeaders(SpdyHeaderBlock& dest, const BalsaHeaders& headers);
  size_t SendSynStreamImpl(uint32 stream_id, const BalsaHeaders& headers);
  size_t SendSynReplyImpl(uint32 stream_id, const BalsaHeaders& headers);
  void SendDataFrameImpl(uint32 stream_id,
                         const char* data,
                         int64 len,
                         SpdyDataFlags flags,
                         bool compress);
  void EnqueueDataFrame(DataFrame* df);
  void GetOutput() override;

 private:
  scoped_ptr<BufferedSpdyFramer> buffered_spdy_framer_;
  bool valid_spdy_session_;  // True if we have seen valid data on this session.
                             // Use this to fail fast when junk is sent to our
                             // port.

  SMConnection* connection_;
  OutputList* client_output_list_;
  OutputOrdering client_output_ordering_;
  uint32 next_outgoing_stream_id_;
  EpollServer* epoll_server_;
  FlipAcceptor* acceptor_;
  MemoryCache* memory_cache_;
  std::vector<SMInterface*> server_interface_list;
  std::vector<int32> unused_server_interface_list;
  typedef std::map<uint32, SMInterface*> StreamToSmif;
  StreamToSmif stream_to_smif_;
  bool close_on_error_;

  static std::string forward_ip_header_;
};

}  // namespace net

#endif  // NET_TOOLS_FLIP_SERVER_SPDY_INTERFACE_H_
